Conversation
Greptile SummaryThis PR introduces end-to-end LEAPP export support for RSL-RL manager-based environments and a new Key changes and concerns:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User / Script
participant EX as export.py
participant EA as ExportPatcher
participant ENV as ManagerBasedRLEnv
participant LP as leapp (annotate)
participant DD as DirectDeploymentEnv
participant IM as InferenceManager (LEAPP)
Note over U,LP: Export Flow
U->>EX: run export.py --task --use_pretrained_checkpoint
EX->>EA: patch_env_for_export(env, task_name, method)
EA->>ENV: patch obs_manager, action_manager, history buffers
EX->>LP: leapp.start(task_name)
EX->>ENV: env.reset()
loop validation_steps
EX->>ENV: policy(obs) [traced through annotated proxies]
ENV-->>LP: annotate.input_tensors(state reads)
ENV-->>LP: annotate.output_tensors(action writes)
EX->>ENV: env.step(actions)
end
EX->>LP: leapp.stop()
EX->>LP: leapp.compile_graph() → .onnx + .yaml
Note over U,IM: Deployment Flow
U->>DD: DirectDeploymentEnv(cfg, leapp.yaml)
DD->>IM: InferenceManager(leapp.yaml)
DD->>DD: _resolve_io() [parse state:/command:/write: connections]
loop simulation running
DD->>DD: _read_inputs() [scene.data.property / command_manager]
DD->>IM: run_policy(inputs)
IM-->>DD: outputs
DD->>DD: _write_outputs() [entity.set_joint_*_target_*]
DD->>DD: decimation physics loop
end
Reviews (1): Last reviewed commit: "precommit updates" | Re-trigger Greptile |
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot
PR: #5105 — Feature/LEAPP Export Integration
Author: @frlai | Changes: +2684/−56 across 33 files
Summary
This PR adds end-to-end LEAPP export functionality for RSL-RL policies trained in manager-based Isaac Lab environments, plus a new DirectDeploymentEnv that can replay exported policies by bypassing all manager classes. The approach uses proxy objects to intercept tensor reads/writes during a tracing pass, then annotates them with semantic metadata for LEAPP graph compilation.
Core new modules:
isaaclab/utils/leapp/— export annotator, proxy chain, semantic metadata decorators, utility helpersisaaclab/envs/direct_deployment_env.py— lightweight deployment env wired via YAML connection stringsscripts/reinforcement_learning/rsl_rl/export.py— export scriptscripts/reinforcement_learning/deploy.py— deployment scriptisaaclab_rl/test/test_rsl_rl_export_flow.py— integration tests (60+ tasks)
Cross-cutting changes:
@leapp_tensor_semanticsdecorators on ~150+ properties across articulation, rigid object, rigid object collection, contact sensor, frame transformer, IMU, and ray caster data classesCircularBufferrewritten from pointer-based ring to shift-based append (for traceability)RayCasterDatafields converted from plain attributes to@property+ private backing fieldsCommandTermCfgextended withcmd_kindandelement_names- Minor observation function fixes for tracing compatibility (humanoid, deploy, dexsuite)
Architecture Assessment
Strengths:
- The proxy-based approach (
_EnvProxy→_SceneProxy→_EntityProxy→_DataProxy) is genuinely elegant — it avoids modifying any concrete data class implementations and resolves semantics lazily via MRO walking. - The shared dedup cache between observation and action paths ensures a single LEAPP input edge for properties read from both sides.
- The
isaaclab_connectionprotocol in the YAML provides a clean deployment contract between export and inference. - Separating export annotation (
export_annotator.py) from deployment wiring (direct_deployment_env.py) is a good architectural choice.
Concerns:
-
Hard dependency on
leappat import time in core modules. Every data class (base_articulation_data.py,base_rigid_object_data.py, all sensor data classes) now importsfrom leapp import InputKindEnumandfrom isaaclab.utils.leapp.leapp_semantics import ...at module level. This meansleappbecomes a mandatory dependency for anyone who imports Isaac Lab, even if they never intend to export. This is a significant change to the dependency footprint. Consider making the import conditional or lazy (e.g.,TYPE_CHECKINGguard for the enum types, with the decorator doing a runtime check). -
Missing
__init__.pyforisaaclab/utils/leapp/— the package directory has no__init__.py. While implicit namespace packages work in Python 3.3+, Isaac Lab consistently uses explicit__init__.pyfiles. This should be added for consistency and to define the public API. -
CircularBufferrewrite has subtle behavioral change. The shift fromtorch.rolltotorch.cat([self._buffer[1:], data.unsqueeze(0)], dim=0)allocates a new tensor on every append (O(max_len) copy). The old implementation did O(1) pointer-based writes. For large buffers or high-frequency use, this is a performance regression. The tradeoff is valid for export traceability, but it should be documented and benchmarked. Consider keeping the original O(1) path for training and only switching to the traceable path during export.
Code Quality Issues
-
Typo in
export.pyline 279:vilidate = args_cli.validation_steps > 0— should bevalidate. -
export.pydocstring says "Train an RL agent" (line 35) — should say "Export an RL agent checkpoint". -
RayCasterData.quat_wdocstring inconsistency: The new property docstring says(w, x, y, z)but the original field and Isaac Lab convention use(x, y, z, w). TheQUAT_WXYZ_ELEMENT_NAMESalso uses["qw", "qx", "qy", "qz"]. Clarify which convention is actually used. -
CommandTermCfgfields lack proper docstrings. The newcmd_kindandelement_namesfields have only inline comments. They should have proper RST-formatted docstrings like other fields in the class. -
_first_param_nameindirect_deployment_env.pyworks correctly for bound methods (whereinspect.signaturestripsself), but add a comment clarifying that bound methods are expected input. -
direct_deployment_env.pyhardcodesnum_envs = 1. This is reasonable for deployment but should be documented as a deliberate constraint.
Export Correctness
-
ensure_torch_tensorsilently swallows conversion errors. Inutils.py, theexcept Exceptioncatch-all means that if warp-to-torch conversion fails for any reason, the raw warp array is passed through silently. This could lead to hard-to-debug issues. At minimum, log a warning. -
patch_warp_to_torch_passthroughmutates a global function (wp.to_torch). This side-effect persists for the entire process lifetime with no way to unpatch. Consider using a context manager pattern. -
History buffer patching (
_patch_history_buffer_append) replaces_appendon the instance. The newCircularBuffer._appendmethod seems designed specifically for this monkey-patching, but it is not documented as an extension point. If the buffer implementation changes, this silently breaks. -
torch.jit._state.disable()at module level inexport.pydisables TorchScript for the entire process. This is extremely aggressive — if any other code in the pipeline relies on JIT compilation, it breaks silently.
Test Coverage
The test file (test_rsl_rl_export_flow.py) is a solid integration test suite covering 60+ tasks. However:
-
Tests require pretrained checkpoints — they skip if unavailable. The PR checklist explicitly notes "Edit tests to work without pretrained checkpoints" as a TODO. This means the test suite may be effectively a no-op in CI.
-
No unit tests for the core modules:
export_annotator.py,proxy.py,leapp_semantics.py,direct_deployment_env.py, or theCircularBufferchanges. The proxy chain and cache deduplication logic are complex enough to warrant targeted unit tests. -
No deployment test —
DirectDeploymentEnvis completely untested.
Documentation
The PR checklist acknowledges missing documentation:
- "Add documentation" — TODO
- "Add integration into direct environment documentation" — TODO
For a feature of this scope, documentation is critical before merge.
CI Status
✅ labeler — pass (only job that ran)
No lint, build, or test CI jobs executed. This is concerning for a 33-file, 2600+ line PR.
Verdict
The architecture is well-designed and the proxy-based annotation approach is clever. Key items to address:
| Priority | Issue |
|---|---|
| P0 | Hard leapp dependency in core modules — must be optional/lazy |
| P0 | Missing __init__.py for isaaclab/utils/leapp/ |
| P1 | CircularBuffer performance regression — document or mitigate |
| P1 | Typo: vilidate → validate |
| P1 | No unit tests for core proxy/annotation logic |
| P2 | Documentation required per contribution guidelines |
| P2 | ensure_torch_tensor silent error swallowing |
| P2 | Global side effects (wp.to_torch mutation, torch.jit disable) need documentation |
CircularBuffer implementation impact study.A test was done to evaluate the impact of the changes related to circular buffer. The new implementation uses a torch.roll on append instead of the original pointer appending and torch.roll buffer selection method. The new change was implemented to allow tracing to happen (original pointer selection uses python logic and is invisible to pytorch). Exparament:To see actual impact, a test was done on Isaac-Velocity-Flat-Spot-v0 which uses the DelayedPDActuator each step. other configurations are as follows: Results
VerdictTest shows no slowdowns and in this test is actually slightly faster across the board (though the difference is statistically insignificant) |
There was a problem hiding this comment.
🤖 IsaacLab Review Bot - Full PR Review
This is a thorough review of the LEAPP export integration feature (#5105). The PR introduces significant new functionality for exporting trained RL policies with semantic metadata for deployment.
Overall Assessment
Excellent work! This is a well-structured and comprehensive feature addition that introduces:
- LEAPP Export Integration - New annotation framework for manager-based RL environments using RSL-RL
DirectDeploymentEnv- Runtime for executing LEAPP-exported policies in simulation- Semantic Annotations - Comprehensive
@leapp_tensor_semanticsdecorators across all asset data classes (articulations, rigid objects, sensors) - Export Scripts -
export.pyanddeploy.pyfor the export and deployment workflow - Documentation - New tutorials and guides for both manager-based and direct deployment exports
- Tests - Integration tests covering multiple task configurations
Summary of Findings
| Severity | Count | Description |
|---|---|---|
| 🔴 Critical | 0 | None |
| 🟡 Warning | 4 | Documentation placeholders, API considerations |
| 🟢 Suggestion | 5 | Code improvements, documentation clarity |
Detailed Findings
🟡 Documentation Placeholders (Warning)
File: docs/source/policy_deployment/04_leapp/exporting_policies_with_leapp.rst
The documentation contains several placeholder links that need to be updated before release:
LEAPP_REPO_LINK_PLACEHOLDERappears multiple times- Installation instructions marked as TODO pending LEAPP public release
Recommendation: Add a tracking issue or ensure these are addressed when LEAPP becomes publicly available.
🟡 Circular Buffer Changes (Warning)
File: source/isaaclab/isaaclab/utils/buffers/circular_buffer.py
The buffer implementation was changed from pointer-based to torch.roll-based for tracing compatibility. This is necessary for LEAPP export but has implications:
def _append(self, data: torch.Tensor):
self._buffer = torch.roll(self._buffer, shifts=-1, dims=0)
self._buffer[-1] = dataConsiderations:
torch.rollcreates a new tensor each time vs. in-place write- May impact performance in high-frequency simulation loops
- The change is well-documented in the class docstring update ✓
🟡 RayCasterData Property Refactor (Warning)
File: source/isaaclab/isaaclab/sensors/ray_caster/ray_caster_data.py
Public attributes converted to properties with private backing stores:
pos_w→_pos_wquat_w→_quat_wray_hits_w→_ray_hits_w
This is required for LEAPP semantics but could break code that assigns to these attributes directly. The CHANGELOG mentions this, which is good.
🟡 Test Timeout Configuration (Warning)
File: source/isaaclab_rl/test/export/test_rsl_rl_export_flow.py
timeout=600,The 600-second timeout is hardcoded. For CI environments or slower machines running complex exports, this may be insufficient.
Suggestion: Consider environment variable override or per-task timeout configuration.
🟢 Tracing-Compatible Refactors (Good)
File: source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/deploy/mdp/observations.py
Excellent change from boolean indexed assignment to torch.where:
# Before (not traceable)
positive_quat[w_negative] = -base_quat[w_negative]
# After (traceable)
positive_quat = torch.where(w_negative.unsqueeze(-1), -base_quat, base_quat)Similar good fix in humanoid/mdp/observations.py replacing to_target_pos[:, 2] = 0.0.
🟢 Proxy Architecture (Good Design)
Files: source/isaaclab/isaaclab/utils/leapp/proxy.py, export_annotator.py
The proxy-based approach for intercepting tensor reads/writes is well-designed:
_EnvProxy→_SceneProxy→_EntityProxy→_DataProxychain_ArticulationWriteProxyfor action output capture- Shared dedup cache prevents duplicate LEAPP edges
- Lazy resolution via MRO walking for semantic metadata
🟢 Semantic Decorator Pattern (Good)
File: source/isaaclab/isaaclab/utils/leapp/leapp_semantics.py
The @leapp_tensor_semantics decorator with InputKindEnum and resolver callbacks is a clean pattern:
@leapp_tensor_semantics(
kind=InputKindEnum.JOINT_POSITION,
element_names_resolver=joint_names_resolver
)The fallback sentinel class when LEAPP isn't installed is a nice touch for maintaining importability.
🟢 Command Semantics Defaults (Good)
Multiple command classes now set default semantic metadata:
self.cfg.cmd_kind = self.cfg.cmd_kind or "command/body/velocity"
self.cfg.element_names = self.cfg.element_names or ["lin_vel_x", "lin_vel_y", "ang_vel_z"]This ensures exported policies have proper semantic information for downstream consumers.
Minor Suggestions
-
Export Script Import Error Handling
Consider wrapping the LEAPP import with a helpful error message if the package isn't installed. -
Logging in
_disable_training_managers
Debug-level logging when disabling curriculum/reward/termination managers would aid troubleshooting. -
DirectDeploymentEnv Documentation
The tutorial mentions a follow-up guide - ensure this is tracked.
Conclusion
This PR is ready for merge pending:
- Resolution of documentation placeholder links (can be done in follow-up if LEAPP isn't public yet)
- Confirmation that the circular buffer performance change is acceptable for production use
Great work on this substantial feature addition! 🎉
| @property | ||
| @leapp_tensor_semantics(kind="state/sensor/position", element_names=XYZ_ELEMENT_NAMES) | ||
| def pos_w(self) -> torch.Tensor: | ||
| """Position of the sensor origin in world frame.""" |
There was a problem hiding this comment.
🟡 Breaking change: property conversion
These attributes have been converted from plain attributes to properties with private backing stores:
# Before
self.pos_w = ...
# After
self._pos_w = ...
@property
def pos_w(self):
return self._pos_wThis is required for LEAPP semantic decorators but is technically a breaking change - any external code doing data.pos_w = value will now fail with AttributeError.
The CHANGELOG mentions this change ✓ but consider whether a setter property should be added for backward compatibility, or if this is an acceptable API change.
There was a problem hiding this comment.
LEAPP Export Integration Review
This is a substantial PR that adds LEAPP (Lightweight Export Annotations for Policy Pipelines) export support to Isaac Lab. Overall, the architecture is well thought out with proxy-based patching for observation and action terms, shared tensor caching for deduplication, and proper semantic metadata for exported tensors.
Summary
Strengths:
- Clean separation between export-time annotation and runtime behavior
- Shared dedup cache prevents duplicate LEAPP edges
- Good documentation with tutorials and API reference
- Comprehensive test coverage with both manager-based and direct environment tests
- Proper handling of recurrent policies and feedback state
Areas for Improvement:
- Several potential edge cases in error handling
- Some hardcoded values that could be configuration options
- Minor type safety and code quality issues
Inline Comments Follow
|
|
||
| .. admonition:: TODO | ||
| :class: warning | ||
|
|
There was a problem hiding this comment.
TODO Items: Multiple TODO admonitions should be tracked in an issue to ensure they're addressed before the feature is considered complete. Consider creating a tracking issue.
Additional Findings (Supplementary)Some additional observations that didn't fit in inline comments: Positive Patterns
Suggestions
# positive_quat = base_quat.clone()
# positive_quat[w_negative] = -base_quat[w_negative]
Questions for Authors
|
…ment for non-export related tasks.
… for tutorial. still need to write the tutorial
5d14bd1 to
cb2622c
Compare
| @@ -0,0 +1,74 @@ | |||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | |||
There was a problem hiding this comment.
would it be clean to place this under scripts/reinforcement_learning/leapp/deploy.py?
There was a problem hiding this comment.
In that case would you want to place all the export scripts under there too?
could be
scripts/reinforcement_learning/leapp/
-> deploy.py
-> rsl_rl/
->export.py
-> other_library/
->export.py
|
|
||
| import warp as wp | ||
|
|
||
| from isaaclab.utils.leapp import ( |
There was a problem hiding this comment.
this won't trigger the full leapp import right?
There was a problem hiding this comment.
it shouldn't. I verified by uninstalling leapp from my environment. training tasks still works.
| # ══════════════════════════════════════════════════════════════════ | ||
|
|
||
|
|
||
| def _resolve_joint_ids(element_names: list | None, entity: Any) -> list[int] | None: |
There was a problem hiding this comment.
could you follow the style and add the parameters to the docstring as well?
|
|
||
| with use_stage(self.sim.stage): | ||
| self.scene = InteractiveScene(cfg.scene) | ||
| with use_stage(self.sim.stage): |
There was a problem hiding this comment.
is this line necessary?
There was a problem hiding this comment.
with use_stage was copied from the other environments. it seems to be used each time the scene and sim are accessed. I also copied the reset itself from the managed environment though i will admit I don't know why we force a reset. it seems to work fine so far.
| in as a constant. | ||
| """ | ||
|
|
||
| def __init__(self, export_method: str, required_obs_groups: set[str] | None = None): |
There was a problem hiding this comment.
can you add some docstrings to all the functions?
| self.success_visualizer = VisualizationMarkers(self.cfg.success_visualizer_cfg) | ||
| self.success_visualizer.set_visibility(True) | ||
|
|
||
| self.cfg.cmd_kind = self.cfg.cmd_kind or "command/body/pose" |
There was a problem hiding this comment.
can you add a comment to why this is needed?
kellyguo11
left a comment
There was a problem hiding this comment.
@AntoineRichard @pascal-roth fyi on the RayCaster changes in this PR
Description
FEATURE: adds new LEAPP export functionality that targets manged environments that using rsl_rl. Policies can be exported end to end. Also adds a new direct deployment environment to deploy exported policies, bypassing all the manager classes.
Type of change
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists thereAdditional TODOs: